Skip to content

feat(wallet): strict address validation in forest-wallet CLI#6968

Open
0xDevNinja wants to merge 1 commit intoChainSafe:mainfrom
0xDevNinja:0xdevninja/issue-6012-wallet-strict-address
Open

feat(wallet): strict address validation in forest-wallet CLI#6968
0xDevNinja wants to merge 1 commit intoChainSafe:mainfrom
0xDevNinja:0xdevninja/issue-6012-wallet-strict-address

Conversation

@0xDevNinja
Copy link
Copy Markdown

@0xDevNinja 0xDevNinja commented Apr 24, 2026

Summary of changes

Changes introduced in this pull request:

  • Migrate address arguments in forest-wallet subcommands to StrictAddress (parsed at clap time). Covers balance, export, has, delete, set-default, sign, verify, and the --from option of send. Handlers destructure the typed value via field: StrictAddress(inner); runtime StrictAddress::from_str(...)? fallbacks are removed.
  • Move network detection ahead of Cli::parse_from in src/wallet/main.rs so that clap-time StrictAddress validation accepts testnet (t0...) addresses when the daemon reports a testnet chain. Client construction errors propagate (mirroring forest-cli after feat: strict checks for address args in forest-cli #6011); an unreachable daemon leaves the global CurrentNetwork at its mainnet default.
  • Tighten wallet_default_address() return type to Option<Address> (was Option<String>), removing a stringify→reparse round trip through StrictAddress::from_str in both the list and send handlers.
  • Add two clap-level regression tests (one for balance, one for sign -a ...) asserting ValueValidation on malformed addresses, guarding against an accidental revert.
  • CHANGELOG.md: Added entry listing the migrated subcommands and explicitly calling out the send target_address non-migration.

Intentional non-migrations

  • send positional target_address stays String: resolve_target_address accepts both FIL (f0.../t0...) and ETH (0x...) forms, but StrictAddress::FromStr rejects the ETH form, so typing it would break the existing ETH-recipient path. Deviates from the issue body, which assumed all address fields could be StrictAddress.
  • validate-address positional address stays String: the purpose of the subcommand is to validate arbitrary user input.

Reference issue to close (if applicable)

Closes #6012

Other information and links

  • Reference PR for the pattern: feat: strict checks for address args in forest-cli #6011 (same change for forest-cli, including the pre-parse network detection).
  • Follow-up opportunity: a typed Recipient enum (Fil(StrictAddress), Eth(EthAddress)) for send target_address, so the ETH form gets the same compile-time check.

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code adheres to the team's documentation standards,
  • I have added tests that prove my fix is effective or that my feature works (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes should be reflected in this document.

Outside contributions

  • I have read and agree to the CONTRIBUTING document.
  • I have read and agree to the AI Policy document. I understand that failure to comply with the guidelines will lead to rejection of the pull request.

AI Usage Disclosure

This PR was prepared with assistance from Claude Code (Anthropic). Extent:

  • Scoping (reading the linked issue + reference PR feat: strict checks for address args in forest-cli #6011), implementation, two self-review passes with an independent reviewer agent, test design, and PR drafting were AI-assisted.
  • All changes were compiled and tested locally by me before pushing — cargo fmt --all -- --check, cargo clippy --profile quick --all-targets --no-deps -- -D warnings (with FOREST_F3_SIDECAR_FFI_BUILD_OPT_OUT=1), and cargo test --lib -- wallet::subcommands (12/12 pass) are all green.
  • End-to-end CLI verification against a running daemon has not been performed locally; the change is pure clap-parse typing and a return-type tightening, both covered by the regression tests.
  • I have reviewed every diff line and take full responsibility for correctness.

Summary by CodeRabbit

Release Notes

  • New Features

    • Improved error messaging for invalid address arguments across wallet commands (balance, export, has, delete, set-default, sign, verify, and send).
    • Network-prefix mismatches now detected and rejected during CLI parsing.
    • Ethereum address format support retained for send command's target address.
  • Tests

    • Added test coverage for address argument validation in wallet commands.

Mirrors the `forest-cli` treatment introduced in ChainSafe#6011 for the
`forest-wallet` binary. Address arguments to `balance`, `export`, `has`,
`delete`, `set-default`, `sign`, `verify` and the `--from` option of
`send` are now parsed into `StrictAddress` at CLI-parse time. clap
surfaces a `ValueValidation` error on a malformed input instead of
deferring to a runtime `StrictAddress::from_str(...)?` fallback, and
each handler simply destructures the typed value via
`field: StrictAddress(inner)`.

Network detection is moved ahead of `Cli::parse_from` so that clap-time
`StrictAddress` validation accepts testnet (`t0...`) addresses when the
daemon reports a testnet chain. Client construction errors propagate
(matching `forest-cli`); an unreachable daemon leaves the global
`CurrentNetwork` at its mainnet default.

`wallet_default_address()` now returns `Option<Address>` rather than
`Option<String>`, removing a stringify-then-reparse round trip through
`StrictAddress::from_str` in both the `list` and `send` handlers.

`Send.target_address` is intentionally kept as a `String` because it
accepts ETH (`0x...`) recipients, which `StrictAddress` rejects;
`resolve_target_address` continues to handle the dispatch.
`ValidateAddress.address` also stays a `String`, since its purpose is
to validate arbitrary user input.

Two clap-level regression tests guard against an accidental revert of
the `StrictAddress` typing.

Refs ChainSafe#6012
@0xDevNinja 0xDevNinja requested a review from a team as a code owner April 24, 2026 13:53
@0xDevNinja 0xDevNinja requested review from LesnyRumcajs and hanabi1224 and removed request for a team April 24, 2026 13:53
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Walkthrough

This PR implements strict address validation in the forest-wallet CLI by shifting address parsing from runtime to compile-time. It uses typed addresses (StrictAddress) for command parameters, adds early network detection before CLI parsing to set the correct network context, and includes tests verifying malformed addresses are rejected during parsing.

Changes

Cohort / File(s) Summary
Documentation
CHANGELOG.md
Added entry documenting the new strict address parsing behavior for forest-wallet CLI, with exception for send's positional target_address remaining a raw string.
Core CLI Infrastructure
src/wallet/main.rs
Reorganized network detection to occur before CLI parsing by initializing rpc::Client early to detect the daemon's network and set global CurrentNetwork, enabling proper address validation context during argument parsing.
Command Definitions & Handlers
src/wallet/subcommands/wallet_cmd.rs
Migrated address parameters from String to StrictAddress types across Balance, Export, Has, SetDefault, Sign, Verify, and Delete commands. Updated WalletBackend::wallet_default_address to return Option<Address> directly. Kept Send.target_address as String to support ETH recipients.
Tests
src/wallet/subcommands/mod.rs
Added unit tests exercising CLI parsing for wallet subcommands, verifying that malformed address arguments are rejected by clap during parsing and extracting clap's ErrorKind from validation failures.

Sequence Diagram

sequenceDiagram
    participant Main as main.rs
    participant RPC as rpc::Client
    participant GlobalNet as GlobalNetwork
    participant CLIParser as CLI Parser
    participant Handler as Command Handler

    rect rgba(100, 150, 200, 0.5)
    Note over Main,GlobalNet: NEW: Early Network Detection (Before Parsing)
    Main->>RPC: Initialize client (no token)
    RPC-->>Main: Connected to daemon
    Main->>RPC: Detect daemon's network chain
    RPC-->>Main: Return NetworkChain
    Main->>GlobalNet: Set CurrentNetwork
    end

    rect rgba(150, 100, 200, 0.5)
    Note over CLIParser,Handler: CLI Parsing with Address Validation
    Main->>CLIParser: Parse CLI args with CurrentNetwork set
    CLIParser->>CLIParser: Validate StrictAddress fields<br/>(network prefix checks)
    alt Address Valid
        CLIParser-->>Main: Return parsed command
        Main->>Handler: Execute command with typed addresses
    else Address Invalid
        CLIParser-->>Main: Return validation error
    end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • hanabi1224
  • LesnyRumcajs
  • akaladarshi
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(wallet): strict address validation in forest-wallet CLI' accurately summarizes the main change: implementing strict address validation in the forest-wallet CLI commands.
Linked Issues check ✅ Passed The pull request implements all coding requirements from issue #6012: migrating address parameters to StrictAddress types for balance, export, has, delete, set-default, sign, verify, and send.from; performing early network detection before CLI parsing; and updating wallet_default_address return type.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue requirements: address type migration, network detection refactoring, and return type updates. The intentional preservation of send.target_address as String is documented and justified.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/wallet/main.rs (1)

24-28: Add a debug/warn log when the network probe fails and mainnet fallback is used.

On Line 24, probe failures are silently ignored; a small log here would make prefix-validation fallbacks much easier to diagnose.

Suggested adjustment
-    if let Ok(name) = StateNetworkName::call(&client, ()).await
-        && !matches!(NetworkChain::from_str(&name), Ok(NetworkChain::Mainnet))
-    {
-        CurrentNetwork::set_global(Network::Testnet);
-    }
+    match StateNetworkName::call(&client, ()).await {
+        Ok(name) if !matches!(NetworkChain::from_str(&name), Ok(NetworkChain::Mainnet)) => {
+            CurrentNetwork::set_global(Network::Testnet);
+        }
+        Ok(_) => {}
+        Err(err) => {
+            tracing::debug!(
+                %err,
+                "network probe failed; keeping CurrentNetwork at mainnet default for CLI parsing"
+            );
+        }
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/main.rs` around lines 24 - 28, The probe using
StateNetworkName::call(&client, ()).await currently swallows failures; update
the branch around StateNetworkName::call, NetworkChain::from_str and the
fallback that calls CurrentNetwork::set_global(Network::Testnet) to log a
warning or debug message when the call fails or when parsing does not yield
Mainnet. Specifically, capture the Err from StateNetworkName::call and log the
error (including the error object and context like "network probe failed,
falling back to Testnet"), and also log when NetworkChain::from_str returns
non-Mainnet to explain why CurrentNetwork::set_global(Network::Testnet) is
invoked.
src/wallet/subcommands/mod.rs (1)

48-69: Add a clap regression test for send --from malformed addresses.

balance and sign are covered, but send --from was also migrated to StrictAddress. A matching ValueValidation test would prevent regressions in that path.

Suggested test addition
     #[test]
     fn wallet_sign_rejects_malformed_address() {
         assert_eq!(
             parse_err_kind(&[
                 "forest-wallet",
                 "sign",
                 "-m",
                 "deadbeef",
                 "-a",
                 "not-an-address",
             ]),
             clap::error::ErrorKind::ValueValidation,
         );
     }
+
+    #[test]
+    fn wallet_send_from_rejects_malformed_address() {
+        assert_eq!(
+            parse_err_kind(&[
+                "forest-wallet",
+                "send",
+                "--from",
+                "not-an-address",
+                "f01234",
+                "1",
+            ]),
+            clap::error::ErrorKind::ValueValidation,
+        );
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/wallet/subcommands/mod.rs` around lines 48 - 69, Add a new unit test
similar to wallet_balance_rejects_malformed_address and
wallet_sign_rejects_malformed_address that asserts parse_err_kind returns
clap::error::ErrorKind::ValueValidation for a malformed address passed to the
send command's --from flag; use the existing helper parse_err_kind and name the
test wallet_send_from_rejects_malformed_address, and supply the minimal extra
send arguments (e.g. to and amount) so the clap parser reaches --from
validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/wallet/main.rs`:
- Around line 24-28: The probe using StateNetworkName::call(&client, ()).await
currently swallows failures; update the branch around StateNetworkName::call,
NetworkChain::from_str and the fallback that calls
CurrentNetwork::set_global(Network::Testnet) to log a warning or debug message
when the call fails or when parsing does not yield Mainnet. Specifically,
capture the Err from StateNetworkName::call and log the error (including the
error object and context like "network probe failed, falling back to Testnet"),
and also log when NetworkChain::from_str returns non-Mainnet to explain why
CurrentNetwork::set_global(Network::Testnet) is invoked.

In `@src/wallet/subcommands/mod.rs`:
- Around line 48-69: Add a new unit test similar to
wallet_balance_rejects_malformed_address and
wallet_sign_rejects_malformed_address that asserts parse_err_kind returns
clap::error::ErrorKind::ValueValidation for a malformed address passed to the
send command's --from flag; use the existing helper parse_err_kind and name the
test wallet_send_from_rejects_malformed_address, and supply the minimal extra
send arguments (e.g. to and amount) so the clap parser reaches --from
validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3f00574d-3b2b-42b7-be1a-1f0963ceca9c

📥 Commits

Reviewing files that changed from the base of the PR and between 65c39a0 and 90b968b.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/wallet/main.rs
  • src/wallet/subcommands/mod.rs
  • src/wallet/subcommands/wallet_cmd.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: implement strict address validation in forest-wallet CLI

1 participant